fix(ext-workflow): honor DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES on workflow channels#1085
fix(ext-workflow): honor DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES on workflow channels#1085tezizzm wants to merge 1 commit into
Conversation
…workflow channels Workflow activity payloads over gRPC's 4 MiB default raised RESOURCE_EXHAUSTED because the durabletask channel ignored the message size limit. dapr#1024 plumbed DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES through the Dapr API channel only; the workflow worker and clients still fell back to the gRPC default. Add a get_grpc_channel_options helper that resolves the limit from an explicit max_grpc_message_length kwarg, then the env var, then None, and thread it through WorkflowRuntime, DaprWorkflowClient, and the async DaprWorkflowClient. Unlike dapr#1024, set both send and receive limits symmetrically since workflow payloads cross the channel in both directions. Reuses the existing setting; no new env var. Signed-off-by: Martez Killens <5050479+tezizzm@users.noreply.github.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1085 +/- ##
==========================================
- Coverage 86.63% 82.79% -3.85%
==========================================
Files 84 146 +62
Lines 4473 14945 +10472
==========================================
+ Hits 3875 12373 +8498
- Misses 598 2572 +1974 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes workflow gRPC message-size handling in dapr-ext-workflow so long-running workflows with large activity payloads (or large accumulated orchestration history) can honor DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES (and an explicit max_grpc_message_length kwarg) instead of failing at gRPC’s default 4 MiB limit.
Changes:
- Added
get_grpc_channel_options()utility to resolve workflow gRPC channel options (kwarg → env var → gRPC default), applying limits symmetrically for send/receive. - Threaded
max_grpc_message_length: Optional[int] = Nonethrough workflow runtime and both sync/async workflow clients, passing resolvedchannel_optionsinto durabletask constructors. - Added unit tests covering precedence and symmetric option application for runtime and clients.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| ext/dapr-ext-workflow/dapr/ext/workflow/util.py | Adds get_grpc_channel_options() to compute workflow-channel gRPC max message size options. |
| ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py | Threads max_grpc_message_length into WorkflowRuntime and passes computed channel_options to TaskHubGrpcWorker. |
| ext/dapr-ext-workflow/dapr/ext/workflow/dapr_workflow_client.py | Threads max_grpc_message_length into sync DaprWorkflowClient and passes computed channel_options to TaskHubGrpcClient. |
| ext/dapr-ext-workflow/dapr/ext/workflow/aio/dapr_workflow_client.py | Threads max_grpc_message_length into async DaprWorkflowClient and passes computed channel_options to AsyncTaskHubGrpcClient. |
| ext/dapr-ext-workflow/tests/test_workflow_util.py | Adds tests for get_grpc_channel_options() precedence and symmetric send/receive options. |
| ext/dapr-ext-workflow/tests/test_workflow_runtime.py | Adds tests asserting WorkflowRuntime passes symmetric channel_options derived from kwarg/env var. |
| ext/dapr-ext-workflow/tests/test_workflow_client.py | Adds tests asserting sync DaprWorkflowClient passes symmetric channel_options derived from kwarg/env var. |
| ext/dapr-ext-workflow/tests/test_workflow_client_aio.py | Adds tests asserting async DaprWorkflowClient passes symmetric channel_options derived from kwarg/env var. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Args: | ||
| max_grpc_message_length: Explicit max gRPC message size in bytes. Takes | ||
| precedence over the env-var setting when truthy. | ||
|
|
||
| Returns: | ||
| A sequence of ``(option, value)`` tuples setting both send and receive | ||
| limits, or ``None`` when no limit is configured. | ||
| """ | ||
| size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES or None | ||
| if size is None: | ||
| return None |
There was a problem hiding this comment.
I don't agree with this suggestion.
| size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES or None | ||
| if size is None: | ||
| return None | ||
| return [ | ||
| ('grpc.max_send_message_length', size), | ||
| ('grpc.max_receive_message_length', size), | ||
| ] |
There was a problem hiding this comment.
| size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES or None | |
| if size is None: | |
| return None | |
| return [ | |
| ('grpc.max_send_message_length', size), | |
| ('grpc.max_receive_message_length', size), | |
| ] | |
| size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES | |
| if not size: | |
| return None | |
| return [ | |
| ('grpc.max_send_message_length', size), | |
| ('grpc.max_receive_message_length', size), | |
| ] |
There was a problem hiding this comment.
This allows None & 0 to fall through and return None for both
Description
Long-running workflows whose activity results exceed gRPC's 4 MiB default fail with
RESOURCE_EXHAUSTED: Sent message larger than max (X vs. 4194304). #1024 addedDAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTESand threaded it through the Dapr APIchannel only — it did not cover the workflow (durabletask) channel, so even with
the env var exported, workflows still hit the 4 MiB ceiling once the orchestration
history (input + accumulated activity results) crossed the limit.
This PR mirrors the #1024 pattern in
dapr-ext-workflow:get_grpc_channel_options()helper indapr/ext/workflow/util.py. Resolutionorder: explicit
max_grpc_message_lengthkwarg →settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES→
None(gRPC default).max_grpc_message_length: Optional[int] = Nonekwarg throughthe three workflow gRPC constructors and passes the resolved
channel_options:WorkflowRuntime.__init__→TaskHubGrpcWorkerDaprWorkflowClient.__init__(sync) →TaskHubGrpcClientaio.DaprWorkflowClient.__init__(async) →AsyncTaskHubGrpcClientprecedence.
Unlike #1024, the limit is applied symmetrically to both send and receive, because
workflow activity payloads cross the channel in both directions (worker → daprd carries
activity output too); #1024 only set the receive limit in its env-var branch.
No new environment variable is introduced (reuses #1024's), and no vendored
_durabletask/internals ordapr/proto/files are touched.Issue reference
We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.
Please reference the issue this PR will close: #1084
Checklist
Please make sure you've completed the relevant tasks for this PR, out of the following list: